__author__="Hong Phuc Bui, Sarah Theobald "
__date__ ="$Nov 2, 2011 10:54:44 AM$"

from copy import deepcopy

#: Standard SBOX
SBOX = [
	[0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76],\
	[0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0],\
	[0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15],\
	[0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75],\
	[0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84],\
	[0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf],\
	[0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8],\
	[0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2],\
	[0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73],\
	[0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb],\
	[0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79],\
	[0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08],\
	[0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a],\
	[0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e],\
	[0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf],\
	[0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]
]

#: Reference column
REFCOL = [0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0]
#: Reference row
REFROW = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f]

class Matrix():#
	'''
Die Klasse Matrix repraesentiert einen Eingabe-String als Matrix, die in dem  
Attribut ``matrix`` gespeichert wird. Zur Auswahl stehen laut Spezifikation entweder eine 
:math:`8\\times 8` Matrix oder eine :math:`8\\times 16` Matrix.
	'''
	
	sigma8_p = [0,1,2,3,4,5,6,7]
	sigma8_q = [1,3,5,7,0,2,4,6]
	
	sigma16_p = [0,1,2,3,4,5,6,11]
	sigma16_q = [1,3,5,11,0,2,4,6]
	
	__ROWS__   = 8   # number of rows of the state matrix
	
	def __init__(self,smallSize=True):#
		'''
Konstructor		

:param smallSize: type ``bool``, wenn ``True`` hat die ``self.matrix`` 8 Spalten,
	sonst hat ``matrix`` 16 Spalten.
		'''
		if smallSize:
			self.__COLS__   = 8   # number of columns of the state matrix 
			self.__ROUNDS__ = 10  # number of rounds in the  permutations functions P and Q
			self.sigma_p = Matrix.sigma8_p # sigma p and q 
			self.sigma_q = Matrix.sigma8_q
			self.__BLOCK_SIZE__ = 512
		else :
			self.__COLS__   = 16
			self.__ROUNDS__ = 14
			self.sigma_p = Matrix.sigma16_p
			self.sigma_q = Matrix.sigma16_q
			self.__BLOCK_SIZE__ = 1024
	
	
	def clone(self):
		'''
			Die Funktion gibt eine neue Instanz von sich selbst zurueck.
		'''
		return deepcopy(self)
		
	def mapping(self,inputStr):#
		'''
			Die Funktion bildet aus der Eingabe eine Matrix. 
		    Betraegt die Laenge des Input-Strings nicht genau 64 
		    (:math:`8\\times 8` Matrix) oder 128 (:math:`8\\times 16` Matrix) Bytes,
		    dann wird eine Exception geworfen. (ungueltige Eingabe)
		    
		    :param inputStr: ein String
		    :raise ValueError: wenn die Laenge der ``inpurStr`` nicht in der Matrix passt,
		    	wird eine Exception ``ValueError`` verursacht.
		'''
		# remove all element of matrix.
		self.matrix = []
		if not (len(inputStr) == self.__COLS__*self.__ROWS__):
			msg = "size of matrix is not match size of string, expected " + \
				str(self.__COLS__*self.__ROWS__) + \
				"bytes got " + str(len(inputStr)) + " bytes"
			raise ValueError(msg)
		
		row = [None]*self.__COLS__ 

		for matrix_counter in range(0,self.__ROWS__):
			row = [None]*self.__COLS__
			for counter in range(0,self.__COLS__):
				row[counter] = (ord(inputStr[8*counter + matrix_counter]))
			self.matrix.append(row)	


	def addRoundConstant(self, i, p_type):#
		'''
			In dieser Funktion wird die Operation 
			:math:`A \leftarrow A \oplus C[i]` 
			durchgefuehrt. :math:`A` ist in diesem 
			Fall die Attribut ``matrix`` der Klasse, :math:`C[i]` wird dynamisch angepasst. 
			
			:param i: Die i-te Schleife von 0 bis ``Matrix.__ROUNDS__``; bei
				:math:`8\\times 8` Matrix betraegt ``Matrix.__ROUNDS__``
				10, bei :math:`8\\times 16` Matrix betraegt ``Matrix.__ROUNDS__`` 14
			:param p_type: wenn ``True``, wird ``self.matrix`` fuer :math:`P` verwendet;
				falls ``False``, ``self.matrix`` fuer :math:`Q`.
		'''
		if p_type:
			for col in range (0,self.__COLS__):
				self.matrix[0][col] ^= (col<<4)^i
		else:
			row = 0; col = 0
			# process the first 7 rows
			for row in range(0,self.__ROWS__ -1):
				for col in range(0,self.__COLS__):	
					self.matrix[row][col] ^= (0xff)
					col +=1;
				row += 1
				
			# process the last row
			for col in range(0,self.__COLS__):
				self.matrix[self.__ROWS__-1][col] ^= (col<<4)^0xff^i;
				
	def shiftLine(self,matrix_row, anzahl_shift):#
		'''
			Hilfsfunktion, in der die einzelnen Zeilen der ``matrix`` nach links um eine vorgegebene
			Anzahl von Bytes geshiftet wird.
			
			:param matrix_row: Zeile der ``matrix``
			:param anzahl_shift: vorgegebene Anzahl von Bytes
			
		'''
		anzahl_shift = anzahl_shift % len(matrix_row)
		return matrix_row[anzahl_shift:] + matrix_row[:anzahl_shift]

	def shiftBytes(self,sigma):#
		'''
			Die Funktion shiftet die Zeilen der ``matrix`` um eine vorgegebene Anzahl von Bytes, die in dem 
			Vektor ``sigma`` gespeichert werden. 
		'''
		for i in range(0,self.__ROWS__):
			self.matrix[i] = self.shiftLine(self.matrix[i], sigma[i])

	
	def subBytes(self):#
		'''
			In der Funktion wird die ``self.matrix`` in eine Schleife durchlaufen, um den ``self.matrix`` Wert durch
			den jeweiligen Wert der ``SBOX`` zu ersetzen. Dazu werden die Vektoren
			:py:data:`~pythoncrypt.groestl.matrix.REFCOL` und
			:py:data:`~pythoncrypt.groestl.matrix.REFROW` referenziert,
			um den Index zu finden und somit den Wert der
			:py:data:`~pythoncrypt.groestl.matrix.SBOX` eindeutig zu bestimmen.
		'''
		for i in range(0,self.__ROWS__):#8
			for j in range(0,self.__COLS__):#8
				matrix_value = self.matrix[i][j]
				row_value = (matrix_value & 0x0f)
				col_index = REFROW.index(row_value)
				col_value = (matrix_value & 0xf0)
				row_index = REFCOL.index(col_value)
				self.matrix[i][j] = SBOX[row_index][col_index]
			
	def mixBytes(self):#
		'''
			In dieser Funktion wird die Operation :math:`A \leftarrow B \\times A` 
			im Koerper :math:`\mathbb{F}_{256}` durchgefuehrt. 
			:math:`A` ist die ``matrix``, :math:`B` ist eine zyklische Matrix, 
			die aus dem Vektor :math:`(02,02,03,04,05,03,05,07)` aufgebaut wird. 
		'''
		for i in range(0,self.__COLS__):
			tmp =[None]*self.__ROWS__
			for j in range(0,self.__ROWS__):
				tmp[j] =\
					mul2( self.matrix[(j+0)%self.__ROWS__] [i] )^\
					mul2( self.matrix[(j+1)%self.__ROWS__] [i] )^\
					mul3( self.matrix[(j+2)%self.__ROWS__] [i] )^\
					mul4( self.matrix[(j+3)%self.__ROWS__] [i] )^\
					mul5( self.matrix[(j+4)%self.__ROWS__] [i] )^\
					mul3( self.matrix[(j+5)%self.__ROWS__] [i] )^\
					mul5( self.matrix[(j+6)%self.__ROWS__] [i] )^\
					mul7( self.matrix[(j+7)%self.__ROWS__] [i] )
			for j in range(0,self.__ROWS__):
				self.matrix[j][i] = tmp[j]
		pass	
	
	def showState(self):#
		'''
			Hilfsfunktion, um die ``matrix`` in menschenlesbarer Form auszugeben.
		'''
		for i in range(0,len(self.matrix)):
			row = []
			for j in range(0,len(self.matrix[i])):
				tmp = hex(self.matrix[i][j]).lstrip("0x")
				while len(tmp) < 2: tmp = "0" + tmp
				row.append( tmp )
			print row
	
	def compareToSpecArray(self,array):#
		'''
			Hilfsfunktion zum Debuggen. Dabei wird die ``matrix`` mit einem Array verglichen.
		'''
		if len(array) != self.__COLS__ * self.__ROWS__:
			return False
		else:
			count = 0
			for i in range(0,self.__ROWS__):
				for j in range(0,self.__COLS__):
					if self.matrix[i][j] != array[count]:
						return (False, "error occur at row " + str(i) +"columns " + str(j))
					count +=1
			return (True, "compare OK")
		
	def compareToStrOfByte(self,string):#
		'''
			Hilfsfunktion zum Debuggen. Dabei wird die ``self.matrix`` mit einem String verglichen.
		'''
		if len(string) != self.__COLS__ * self.__ROWS__:
			return False
		else:
			count = 0
			for i in range(0,self.__COLS__):
				for j in range(0,self.__ROWS__):
					if self.matrix[j][i] != ord(string[count]):
						return (False, "error occur at row " + str(j) 
									 + " columns " + str(i)
									 + " expected " + hex(ord(string[count]))
									 + " got " + hex(self.matrix[j][i])
										)
					count +=1
			return (True, "compare OK")
		
		
#####################################################################
# short cut used in function mixBytes
def mul1(x): return x&(0xff)
def mul2(x): 
	if ((0xff & x) >> 7) == 1:
		return  0xff & ((x << 1) ^ 0x1b)
	else:
		return  (x << 1)
def mul3(x):
	return mul2(x)^mul1(x)
def mul4(x):
	return mul2(mul2(x))
def mul5(x):
	return mul4(x)^mul1(x)
def mul6(x):
	return mul4(x)^mul2(x)
def mul7(x):
	return mul4(x)^mul2(x)^mul1(x)			
#######################################################################	



if __name__ == "__main__":
	m = Matrix()
	test = "0123456789qwertzuiopasdfghjklyxcvbnm1234567890qwertzuiopasdfghjk"
	chain = Matrix()
	chain.mapping(test)
	
	test2 = "''''''''"*8

	message = Matrix()
	message.mapping(test2)
	
	print "test: %s" %test
	print "chain:"
	chain.showState()
	
	print "test2: %s" %test2
	print "message:"
	message.showState()
	#result = xOrMatrix(chain, message)
	#print "result:"
	#print result  
	
